home *** CD-ROM | disk | FTP | other *** search
- ------------------------------------------------------------------------------
- -- --
- -- GNAT RUNTIME COMPONENTS --
- -- --
- -- S Y S T E M . I M G _ R E A L --
- -- --
- -- B o d y --
- -- --
- -- $Revision: 1.34 $ --
- -- --
- -- Copyright (C) 1992,1993,1994,1995,1996 Free Software Foundation, Inc. --
- -- --
- -- GNAT is free software; you can redistribute it and/or modify it under --
- -- terms of the GNU General Public License as published by the Free Soft- --
- -- ware Foundation; either version 2, or (at your option) any later ver- --
- -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
- -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
- -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
- -- for more details. You should have received a copy of the GNU General --
- -- Public License distributed with GNAT; see file COPYING. If not, write --
- -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
- -- MA 02111-1307, USA. --
- -- --
- -- As a special exception, if other files instantiate generics from this --
- -- unit, or you link this unit with other files to produce an executable, --
- -- this unit does not by itself cause the resulting executable to be --
- -- covered by the GNU General Public License. This exception does not --
- -- however invalidate any other reasons why the executable file might be --
- -- covered by the GNU Public License. --
- -- --
- -- GNAT was originally developed by the GNAT team at New York University. --
- -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
- -- --
- ------------------------------------------------------------------------------
-
- with System.Img_LLU; use System.Img_LLU;
- with System.Img_Uns; use System.Img_Uns;
- with System.Parameters;
- with System.Powten_Table; use System.Powten_Table;
- with System.Unsigned_Types; use System.Unsigned_Types;
-
- package body System.Img_Real is
-
- -- The following defines the maximum number of digits that we can convert
- -- accurately. This is limited by the precision of Long_Long_Float, and
- -- also by the number of digits we can hold in Long_Long_Unsigned, which
- -- is the integer type we use as an intermediate for the result.
-
- -- We assume that in practice, the limitation will come from the digits
- -- value, rather than the integer value. This is true for typical IEEE
- -- implementations, and at worst, the only loss is for some precision
- -- in very high precision floating-point output.
-
- -- Note that in the following, the "-2" accounts for the sign and one
- -- extra digits, since we need the maximum number of 9's that can be
- -- supported, e.g. for the normal 64 bit case, Long_Long_Integer'Width
- -- is 21, since the maximum value (approx 1.6 * 10**19) has 20 digits,
- -- but the maximum number of 9's that can be supported is 19.
-
- Maxdigs : constant :=
- Natural'Min
- (Long_Long_Unsigned'Width - 2, Long_Long_Float'Digits);
-
- Unsdigs : constant := Unsigned'Width - 2;
- -- Number of digits that can be converted using type Unsigned
- -- See above for the explanation of the -2.
-
- function Is_Negative (V : Long_Long_Float) return Boolean;
- pragma Import (Intrinsic, Is_Negative);
-
- --------------------------------
- -- Image_Ordinary_Fixed_Point --
- --------------------------------
-
- function Image_Ordinary_Fixed_Point
- (V : Long_Long_Float;
- Aft : Natural)
- return String
- is
- P : Natural := 0;
- S : String (1 .. Long_Long_Float'Width);
-
- begin
- if V >= 0.0 then
- S (1) := ' ';
- P := 1;
- end if;
-
- Set_Image_Real (V, S, P, 1, Aft, 0);
- return S (1 .. P);
- end Image_Ordinary_Fixed_Point;
-
- --------------------------
- -- Image_Floating_Point --
- --------------------------
-
- function Image_Floating_Point
- (V : Long_Long_Float;
- Digs : Natural)
- return String
- is
- P : Natural := 0;
- S : String (1 .. Long_Long_Float'Width);
-
- begin
- if not Is_Negative (V) then
- S (1) := ' ';
- P := 1;
- end if;
-
- Set_Image_Real (V, S, P, 1, Digs - 1, 3);
- return S (1 .. P);
- end Image_Floating_Point;
-
- --------------------
- -- Set_Image_Real --
- --------------------
-
- procedure Set_Image_Real
- (V : Long_Long_Float;
- S : out String;
- P : in out Natural;
- Fore : Natural;
- Aft : Natural;
- Exp : Natural)
- is
- NFrac : constant Natural := Natural'Max (Aft, 1);
- Sign : Character;
- X : Long_Long_Float;
- Scale : Integer;
- Expon : Integer;
-
- Digs : String (1 .. 2 * System.Parameters.Field_Max);
- -- Array used to hold digits of converted integer value. This is a
- -- large enough buffer to accomodate ludicrous values of Fore and Aft.
-
- Ndigs : Natural;
- -- Number of digits stored in Digs (and also subscript of last digit)
-
- procedure Adjust_Scale (S : Natural);
- -- Adjusts the value in X by multiplying or dividing by a power of
- -- ten so that it is in the range 10**(S-1) <= X < 10**S. Includes
- -- adding 0.5 to round the result, readjusting if the rounding causes
- -- the result to wander out of the range. Scale is adjusted to reflect
- -- the power of ten used to divide the result (i.e. one is added to
- -- the scale value for each division by 10.0, or one is subtracted
- -- for each multiplication by 10.0).
-
- procedure Convert_Integer;
- -- Takes the value in X, outputs integer digits into Digs. On return,
- -- Ndigs is set to the number of digits stored. The digits are stored
- -- in Digs (1 .. Ndigs),
-
- procedure Set (C : Character);
- -- Sets character C in output buffer
-
- procedure Set_Blanks_And_Sign (N : Integer);
- -- Sets leading blanks and minus sign if needed. N is the number of
- -- positions to be filled (a minus sign is output even if N is zero
- -- or negative, but for a positive value, if N is non-positive, then
- -- the call has no effect).
-
- procedure Set_Digs (S, E : Natural);
- -- Set digits S through E from Digs buffer. No effect if S > E
-
- procedure Set_Special_Fill (N : Natural);
- -- After outputting +Inf, -Inf or NaN, this routine fills out the
- -- rest of the field with * characters. The argument is the number
- -- of characters output so far (either 3 or 4)
-
- procedure Set_Zeros (N : Integer);
- -- Set N zeros, no effect if N is negative
-
- pragma Inline (Set);
- pragma Inline (Set_Digs);
- pragma Inline (Set_Zeros);
-
- ------------------
- -- Adjust_Scale --
- ------------------
-
- procedure Adjust_Scale (S : Natural) is
- Lo : Natural;
- Hi : Natural;
- Mid : Natural;
- XP : Long_Long_Float;
-
- begin
- -- Cases where scaling up is required
-
- if X < Powten (S - 1) then
-
- -- What we are looking for is a power of ten to multiply X by
- -- so that the result lies within the required range.
-
- loop
- XP := X * Powten (Maxpow);
- exit when XP >= Powten (S - 1);
- X := XP;
- Scale := Scale - Maxpow;
- end loop;
-
- -- Here we know that we must mutiply by at least 10**1 and that
- -- 10**Maxpow takes us too far: binary search to find right one.
-
- Lo := 1;
- Hi := Maxpow;
-
- loop
- Mid := (Lo + Hi) / 2;
- XP := X * Powten (Mid);
-
- if XP < Powten (S - 1) then
- Lo := Mid + 1;
-
- elsif XP >= Powten (S) then
- Hi := Mid - 1;
-
- else
- X := XP;
- Scale := Scale - Mid;
- exit;
- end if;
- end loop;
-
- -- Cases where scaling down is required
-
- elsif X >= Powten (S) then
-
- -- What we are looking for is a power of ten to divide X by
- -- so that the result lies within the required range.
-
- loop
- XP := X / Powten (Maxpow);
- exit when XP < Powten (S);
- X := XP;
- Scale := Scale + Maxpow;
- end loop;
-
- -- Here we know that we must divide by at least 10**1 and that
- -- 10**Maxpow takes us too far, binary search to find right one.
-
- Lo := 1;
- Hi := Maxpow;
-
- loop
- Mid := (Lo + Hi) / 2;
- XP := X / Powten (Mid);
-
- if XP < Powten (S - 1) then
- Hi := Mid - 1;
-
- elsif XP >= Powten (S) then
- Lo := Mid + 1;
-
- else
- X := XP;
- Scale := Scale + Mid;
- exit;
- end if;
- end loop;
-
- -- Here we are already scaled right
-
- else
- null;
- end if;
-
- -- Round, readjusting scale if needed. Note that if a readjustment
- -- occurs, then it is never necessary to round again, because there
- -- is no possibility of such a second rounding causing a change.
-
- X := X + 0.5;
-
- if X > Powten (S) then
- X := X / 10.0;
- Scale := Scale + 1;
- end if;
-
- end Adjust_Scale;
-
- ---------------------
- -- Convert_Integer --
- ---------------------
-
- procedure Convert_Integer is
- begin
- -- Use Unsigned routine if possible, since on many machines it will
- -- be significantly more efficient than the Long_Long_Unsigned one.
-
- if X < Powten (Unsdigs) then
- Ndigs := 0;
- Set_Image_Unsigned
- (Unsigned (Long_Long_Float'Truncation (X)),
- Digs, Ndigs);
-
- -- But if we want more digits than fit in Unsigned, we have to use
- -- the Long_Long_Unsigned routine after all.
-
- else
- Ndigs := 0;
- Set_Image_Long_Long_Unsigned
- (Long_Long_Unsigned (Long_Long_Float'Truncation (X)),
- Digs, Ndigs);
- end if;
- end Convert_Integer;
-
- ---------
- -- Set --
- ---------
-
- procedure Set (C : Character) is
- begin
- P := P + 1;
- S (P) := C;
- end Set;
-
- -------------------------
- -- Set_Blanks_And_Sign --
- -------------------------
-
- procedure Set_Blanks_And_Sign (N : Integer) is
- W : Integer := N;
-
- begin
- if Sign = '-' then
- for J in 1 .. N - 1 loop
- Set (' ');
- end loop;
-
- Set ('-');
-
- else
- for J in 1 .. N loop
- Set (' ');
- end loop;
- end if;
- end Set_Blanks_And_Sign;
-
- --------------
- -- Set_Digs --
- --------------
-
- procedure Set_Digs (S, E : Natural) is
- begin
- for J in S .. E loop
- Set (Digs (J));
- end loop;
- end Set_Digs;
-
- ----------------------
- -- Set_Special_Fill --
- ----------------------
-
- procedure Set_Special_Fill (N : Natural) is
- F : Natural;
-
- begin
- F := Fore + 1 + Aft - N;
-
- if Exp /= 0 then
- F := F + Exp + 1;
- end if;
-
- for J in 1 .. F loop
- Set ('*');
- end loop;
- end Set_Special_Fill;
-
- ---------------
- -- Set_Zeros --
- ---------------
-
- procedure Set_Zeros (N : Integer) is
- begin
- for J in 1 .. N loop
- Set ('0');
- end loop;
- end Set_Zeros;
-
- -- Start of processing for Set_Image_Real
-
- begin
- Scale := 0;
-
- -- Positive values
-
- if V > 0.0 then
- X := V;
- Sign := '+';
-
- -- Negative values
-
- elsif V < 0.0 then
- X := -V;
- Sign := '-';
-
- -- Zero values
-
- elsif V = 0.0 then
- if Is_Negative (V) then
- Sign := '-';
- else
- Sign := '+';
- end if;
-
- Set_Blanks_And_Sign (Fore - 1);
- Set ('0');
- Set ('.');
- Set_Zeros (NFrac);
-
- if Exp /= 0 then
- Set ('E');
- Set ('+');
- Set_Zeros (Natural'Max (1, Exp - 1));
- end if;
-
- return;
-
- -- Only NaN's fail all three of the above tests!
-
- else
- Set ('N');
- Set ('a');
- Set ('N');
- Set_Special_Fill (3);
- return;
- end if;
-
- -- If value is greater than Long_Long_Float'Last it is infinite
-
- if X > Long_Long_Float'Last then
- Set (Sign);
- Set ('I');
- Set ('n');
- Set ('f');
- Set_Special_Fill (4);
-
- -- Case of non-zero value with Exp = 0
-
- elsif Exp = 0 then
-
- -- First step is to multiply by 10 ** Nfrac to get an integer
- -- value to be output, an then add 0.5 to round the result.
-
- declare
- NF : Natural := NFrac;
-
- begin
- loop
- -- If we are larger than Powten (Maxdigs) now, then
- -- we have too many significant digits, and we have
- -- not even finished multiplying by NFrac (NF shows
- -- the number of unaccounted-for digits).
-
- if X >= Powten (Maxdigs) then
-
- -- In this situation, we only to generate a reasonable
- -- number of significant digits, and then zeroes after.
- -- So first we rescale to get:
-
- -- 10 ** (Maxdigs - 1) <= X < 10 ** Maxdigs
-
- -- and then convert the resulting integer
-
- Adjust_Scale (Maxdigs);
- Convert_Integer;
-
- -- If that caused rescaling, then add zeros to the end
- -- of the number to account for this scaling. Also add
- -- zeroes to account for the undone multiplications
-
- for J in 1 .. Scale + NF loop
- Ndigs := Ndigs + 1;
- Digs (Ndigs) := '0';
- end loop;
-
- exit;
-
- -- If multiplication is complete, then convert the resulting
- -- integer after rounding (note that X is non-negative)
-
- elsif NF = 0 then
- X := X + 0.5;
- Convert_Integer;
- exit;
-
- -- Otherwise we can go ahead with the multiplication. If it
- -- can be done in one step, then do it in one step.
-
- elsif NF < Maxpow then
- X := X * Powten (NF);
- NF := 0;
-
- -- If it cannot be done in one step, then do partial scaling
-
- else
- X := X * Powten (Maxpow);
- NF := NF - Maxpow;
- end if;
- end loop;
- end;
-
- -- If number of available digits is less or equal to NFrac,
- -- then we need an extra zero before the decimal point.
-
- if Ndigs <= NFrac then
- Set_Blanks_And_Sign (Fore - 1);
- Set ('0');
- Set ('.');
- Set_Zeros (NFrac - Ndigs);
- Set_Digs (1, Ndigs);
-
- -- Normal case with some digits before the decimal point
-
- else
- Set_Blanks_And_Sign (Fore - (Ndigs - NFrac));
- Set_Digs (1, Ndigs - NFrac);
- Set ('.');
- Set_Digs (Ndigs - NFrac + 1, Ndigs);
- end if;
-
- -- Case of non-zero value with non-zero Exp value
-
- else
- -- If NFrac is less than Maxdigs, then all the fraction digits are
- -- significant, so we can scale the resulting integer accordingly.
-
- if NFrac < Maxdigs then
- Adjust_Scale (NFrac + 1);
- Convert_Integer;
-
- -- Otherwise, we get the maximum number of digits available
-
- else
- Adjust_Scale (Maxdigs);
- Convert_Integer;
-
- for J in 1 .. NFrac - Maxdigs + 1 loop
- Ndigs := Ndigs + 1;
- Digs (Ndigs) := '0';
- Scale := Scale - 1;
- end loop;
- end if;
-
- Set_Blanks_And_Sign (Fore - 1);
- Set (Digs (1));
- Set ('.');
- Set_Digs (2, Ndigs);
-
- -- The exponent is the scaling factor adjusted for the digits
- -- that we output after the decimal point, since these were
- -- included in the scaled digits that we output.
-
- Expon := Scale + NFrac;
-
- Set ('E');
- Ndigs := 0;
-
- if Expon >= 0 then
- Set ('+');
- Set_Image_Unsigned (Unsigned (Expon), Digs, Ndigs);
- else
- Set ('-');
- Set_Image_Unsigned (Unsigned (-Expon), Digs, Ndigs);
- end if;
-
- Set_Zeros (Exp - Ndigs - 1);
- Set_Digs (1, Ndigs);
- end if;
-
- end Set_Image_Real;
-
- end System.Img_Real;
-